<?php
/** 
 * EGP Framework
 *
 * LICENSE
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @author     Akon(番茄红了) <aultoale@gmail.com>
 * @copyright  Copyright (c) 2008 (http://www.tblog.com.cn)
 * @license    http://www.gnu.org/licenses/gpl.html     GPL 3
 */

!defined('EGP_LIB_DIR') && die('Access Deny!');

/**
 * ACL DbTable
 *
 * @package    classes
 * @author     Akon(番茄红了) <aultoale@gmail.com>
 * @copyright  Copyright (c) 2008 (http://www.tblog.com.cn)
 * @license    http://www.gnu.org/licenses/gpl.html     GPL 3
 */
class EGP_Acl_DbTable
{

    const ROLE_ID   = 'role_id';
    const ROLE_NAME = 'role_name';
    const RES_ID    = 'res_id';
    const RES_NAME  = 'res_name';
    const RULE_ID   = 'rule_id';
    const PRIVILEGE = 'privilege';
    const PERMIT    = 'permit';

    /**
     * 数据库适配器
     *
     * @var EGP_Db_Adapter_Abstract
     */
    protected $_dbAdapter = null;

    /**
     * 数据库名(方案名)
     *
     * @var string
     */
    protected $_schema = null;

    /**
     * ACL 对象
     *
     * @var Acl
     */
    protected $_acl = null;

    /**
     * DbTable 配置数据
     *
     * @var EGP_ArrayObject
     */
    protected $_config = null;

    /**
     * 构造方法
     *
     * @param  EGP_Db_Adapter_Abstract  $dbAdapter
     * @param  string                   schema
     */
    public function __construct(EGP_Db_Adapter_Abstract $dbAdapter, $schema = null)
    {
        $this->_dbAdapter = $dbAdapter;
        $this->_schema = $schema;
        $this->_config = new EGP_ArrayObject(array(
            'role' => array(
                'table' => 'acl_role',
                'columns' => array(
                    self::ROLE_ID => self::ROLE_ID,
                    self::ROLE_NAME => self::ROLE_NAME,
                )
            ),
            'resource' => array(
                'table' => 'acl_resource',
                'columns' => array(
                    self::RES_ID => self::RES_ID,
                    self::RES_NAME => self::RES_NAME,
                )
            ),
            'rule' => array(
                'table' => 'acl_rule',
                'columns' => array(
                    self::RULE_ID => self::RULE_ID,
                    self::ROLE_ID => self::ROLE_ID,
                    self::RES_ID => self::RES_ID,
                    self::PRIVILEGE => self::PRIVILEGE,
                    self::PERMIT => self::PERMIT,
                )
            )
        ));
    }

    /**
     * 设置角色表参数
     *
     * @param  string  $table
     * @param  array  $columns
     * @return Acl_DbTable
     */
    public function setRoleTable($table, array $columns = array())
    {
        return $this->setTable('role', $table, $columns);
    }

    /**
     * 设置资源表参数
     *
     * @param  string  $table
     * @param  array  $columns
     * @return Acl_DbTable
     */
    public function setResourceTable($table, array $columns = array())
    {
        return $this->setTable('resource', $table, $columns);
    }

    /**
     * 设置规则表参数
     *
     * @param  string  $table
     * @param  array   $columns
     * @return Acl_DbTable
     */
    public function setRuleTable($table, array $columns = array())
    {
        return $this->setTable('rule', $table, $columns);
    }

    /**
     * 设置数据库表参数
     *
     * @param  string  $table
     * @param  string  $tbname
     * @param  array   $columns
     * @return EGP_Acl_DbTable
     * @throws EGP_Exception
     */
    public function setTable($table, $tbname, array $columns = array())
    {
        if (!isset($this->_config->$table))
            throw new EGP_Exception("未知的表配置 '$table'");
        $option = &$this->_config->$table;
        $option->table  = (string) $tbname;
        foreach ($columns as $name => $column) {
            if (isset($option->columns->$name))
                $option->columns->$name = $column;
        }
        return $this;
    }

    /**
     * 获取 ACL 对象
     *
     * @return Acl
     */
    public function getAcl()
    {
        if (!$this->_acl instanceof Acl) {
            $acl = new Acl();

            //添加角色
            $roleConfig = $this->_config->role;
            $roles = $this->_dbAdapter->select()
                ->from(
                    $roleConfig->table,
                    $roleConfig->columns->{self::ROLE_NAME},
                    $this->_schema
                )
                ->fetchAll();
            foreach ($roles as $role)
                $acl->addRole($role[$roleConfig->columns->{self::ROLE_NAME}]);

            //添加资源
            $resConfig = $this->_config->resource;
            $resources = $this->_dbAdapter->select()
                ->from(
                    $resConfig->table,
                    $resConfig->columns->{self::RES_NAME},
                    $this->_schema
                )
                ->fetchAll();
            foreach ($resources as $resource)
                $acl->add($resource[$resConfig->columns->{self::RES_NAME}]);

            //设定规则
            $ruleConfig = $this->_config->rule;
            $rules = $this->_dbAdapter->select()
                ->from(
                    array('RULE' => $ruleConfig->table),
                    array(
                        $ruleConfig->columns->{self::PERMIT},
                        $ruleConfig->columns->{self::PRIVILEGE}
                    ),
                    $this->_schema
                )
                ->join(
                    array('ROLE' => $roleConfig->table),
                    'RULE.' . $ruleConfig->columns->{self::ROLE_ID} . '=' .
                    'ROLE.' . $roleConfig->columns->{self::ROLE_ID},
                    array($roleConfig->columns->{self::ROLE_NAME}),
                    $this->_schema
                )
                ->join(
                    array('RES' => $resConfig->table),
                    'RULE.' . $ruleConfig->columns->{self::RES_ID} . '=' .
                    'RES.' . $resConfig->columns->{self::RES_ID},
                    array($resConfig->columns->{self::RES_NAME}),
                    $this->_schema
                )
                ->fetchAll();
            foreach ($rules as $rule) {
                if ($rule[$ruleConfig->columns->{self::PERMIT}]) {
                    $acl->allow(
                        $rule[$roleConfig->columns->{self::ROLE_NAME}],
                        $rule[$resConfig->columns->{self::RES_NAME}],
                        $rule[$ruleConfig->columns->{self::PRIVILEGE}]
                    );
                } else {
                    $acl->deny(
                        $rule[$roleConfig->columns->{self::ROLE_NAME}],
                        $rule[$resConfig->columns->{self::RES_NAME}],
                        $rule[$ruleConfig->columns->{self::PRIVILEGE}]
                    );
                }
            }

            $this->_acl = $acl;
        }

        return $this->_acl;
    }

}